home *** CD-ROM | disk | FTP | other *** search
/ Scene Storm / Scene Storm - Volume 1.iso / coding / c / amiexpress / source / doors / amixpr / xpr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-26  |  28.3 KB  |  1,273 lines

  1. #include <exec/exec.h>
  2. #include <dos/dos.h>
  3. #include <dos/dosextens.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <devices/serial.h>
  7. #include <intuition/intuition.h>
  8. #include <dos/dosasl.h>
  9. #include "bbs.h"
  10. #include "xproto.h"
  11. #define MILLION 1000000L
  12.  
  13. extern struct IOExtSer *WriteSerReq;
  14. extern struct IOExtSer *ReadSerReq;
  15. struct XPR_IO *XprIO;
  16. struct Library *XProtocolBase;
  17. extern struct MsgPort *ReadSerPort;
  18. extern struct MsgPort *WriteSerPort;
  19. extern struct timerequest    *TimerMsg;
  20. extern struct Window        *ZmodemStats;
  21. extern struct Library *IntuitionBase;
  22. LONG __saveds __asm        xpr_fopen(register __a0 UBYTE *FileName,register __a1 UBYTE *AccessMode);
  23. LONG __saveds __asm        xpr_fclose(register __a0 struct Buffer *File);
  24. LONG __saveds __asm        xpr_fread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File);
  25. LONG __saveds __asm        xpr_fwrite(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File);
  26. ULONG __saveds __asm        xpr_sread(register __a0 UBYTE *Buffer,register __d0 ULONG Size,register __d1 LONG Timeout);
  27. LONG __saveds __asm        xpr_swrite(register __a0 UBYTE *Buffer,register __d0 LONG Size);
  28. LONG __saveds            xpr_sflush(VOID);
  29. LONG __saveds __asm        xpr_update(register __a0 struct XPR_UPDATE *UpdateInfo);
  30. LONG __saveds            xpr_chkabort(VOID);
  31. LONG __saveds __asm        xpr_gets(register __a0 UBYTE *Prompt,register __a1 UBYTE *Buffer);
  32. LONG __saveds __asm        xpr_setserial(register __d0 LONG Status);
  33. LONG __saveds __asm        xpr_ffirst(register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern);
  34. LONG __saveds __asm        xpr_fnext(register __d0 LONG OldState,register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern);
  35. LONG __saveds __asm        xpr_finfo(register __a0 UBYTE *FileName,register __d0 LONG InfoType);
  36. LONG __saveds __asm        xpr_fseek(register __a0 struct Buffer *File,register __d0 LONG Offset,register __d1 LONG Origin);
  37. ULONG __saveds __asm        xpr_options(register __d0 LONG NumOpts,register __a0 struct xpr_option **Opts);
  38. LONG __saveds __asm        xpr_unlink(register __a0 UBYTE *FileName);
  39. LONG __saveds            xpr_squery(VOID);
  40. LONG __saveds __asm        xpr_getptr(register __d0 LONG InfoType);
  41. LONG __saveds __asm        xpr_stealopts(register __a0 UBYTE *Prompt,register __a1 UBYTE *Buffer);
  42.  
  43. VOID __stdargs            TransferInfo(WORD X,WORD Y,BYTE *String,...);
  44. VOID __stdargs            ShowString(struct Gadget *Gadget,UBYTE *String,...);
  45. VOID                ShowStats(struct Gadget *Gadget,LONG Value,LONG Max);
  46.  
  47.  
  48. BYTE                ProtocolSetup(VOID);
  49. VOID                SaveProtocolOpts(VOID);
  50.  
  51. STATIC UBYTE         LineOffsets[] = {  0, 8,16, 0,30,38,46,54, 0,67,75 };
  52.  
  53. #define WIDTH        564
  54. #define HEIGHT        198
  55.  
  56. #define ORIGIN_X    10
  57. #define ORIGIN_Y    (14+50)
  58.  
  59. char LastXprLibrary[]= "xprzmodem.library";
  60. static BOOL UsesZModem;
  61. static BOOL UseNewLibrary;
  62. static BOOL Alerted;
  63. static BOOL DidTransfer;
  64. static BOOL Uploading;
  65. static BOOL SendAbort;
  66. static BOOL MultipleFiles;
  67. static BOOL TransferAborted;
  68. static BOOL BinaryTransfer;
  69. static BYTE FileMatch;
  70. static UBYTE             SharedBuffer[512];
  71. static char *FileArg[100];
  72. static int FileCount,FileCountMax;
  73. static int TransferBits;
  74. static int ByteVal,ByteMax,TimeVal,TimeMax;
  75. static struct Buffer *CurrentFile;
  76. static UBYTE             ConvNumber[30],ConvNumber10[30];
  77. static struct Gadget        *TransferGadgetArray[8];
  78. static struct AnchorPath    *FileAnchor;
  79. static BYTE             ValidTab[256];
  80. static char RealName[200];
  81.  
  82. ULONG __saveds __asm
  83. xpr_options(register __d0 LONG NumOpts,register __a0 struct xpr_option **Opts)
  84. {
  85.     ULONG         Flags = 0;
  86.  
  87.     return(Flags);
  88. }
  89. LONG __saveds __asm
  90. xpr_unlink(register __a0 UBYTE *FileName)
  91. {
  92.     LONG Success = DeleteFile(FileName) ? 0 : -1;
  93.  
  94.     return(Success);
  95. }
  96. LONG __saveds
  97. xpr_squery()
  98. {
  99.     if(WriteSerReq)
  100.     {
  101.         WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;
  102.  
  103.         if(!DoIO(WriteSerReq))
  104.             return((LONG)WriteSerReq -> IOSer . io_Actual);
  105.     }
  106.  
  107.     return(-1);
  108. }
  109.  
  110.     /* xpr_getptr(LONG InfoType):
  111.      *
  112.      *    Return a pointer to the term custom screen.
  113.      */
  114.  
  115. LONG __saveds __asm
  116. xpr_getptr(register __d0 LONG InfoType)
  117. {
  118.      char temp[100];
  119.      gu(temp,RAWSCREEN_ADDRESS);
  120.     if(InfoType == 1)
  121.         return((LONG)atol(temp));
  122.     else
  123.         return(-1);
  124. }
  125. BYTE
  126. ProtocolSetup()
  127. {
  128.     UBYTE NameBuffer[40],i;
  129.  
  130.         /* Close the old library if still open. */
  131.  
  132.     if(XProtocolBase)
  133.     {
  134.         XProtocolCleanup(XprIO);
  135.  
  136.         CloseLibrary(XProtocolBase);
  137.     }
  138.  
  139.         /* Clear the XPR interface buffer. */
  140.  
  141.     memset(XprIO,0,sizeof(struct XPR_IO));
  142.  
  143.         /* Copy the name of the library. */
  144.  
  145.     strcpy(NameBuffer,FilePart(LastXprLibrary));
  146.  
  147.         /* Extract the name itself (strip the `.library'). */
  148.  
  149.     for(i = strlen(NameBuffer) - 1 ; i >= 0 ; i--)
  150.     {
  151.         if(NameBuffer[i] == '.')
  152.         {
  153.             NameBuffer[i] = 0;
  154.             break;
  155.         }
  156.     }
  157.  
  158.         /* Check if the transfer protocol is a sort of ZModem. */
  159.  
  160.     UsesZModem = FALSE;
  161.  
  162.     for(i = 0 ; i <= strlen(NameBuffer) - 6 ; i++)
  163.     {
  164.         if(!Stricmp(&NameBuffer[i],"zmodem"))
  165.             UsesZModem = TRUE;
  166.     }
  167.  
  168.         /* Obtain the protocol default settings. */
  169.  
  170.     //if(!GetEnvDOS(NameBuffer,ProtocolOptsBuffer))
  171.     //    ProtocolOptsBuffer[0] = 0;
  172.  
  173.         /* Initialize the interface structure. */
  174.  
  175.     XprIO -> xpr_filename    = NULL;
  176.     XprIO -> xpr_fopen    = (APTR)xpr_fopen;
  177.     XprIO -> xpr_fclose    = (APTR)xpr_fclose;
  178.     XprIO -> xpr_fread    = (APTR)xpr_fread;
  179.     XprIO -> xpr_fwrite    = (APTR)xpr_fwrite;
  180.     XprIO -> xpr_sread    = (APTR)xpr_sread;
  181.     XprIO -> xpr_swrite    = (APTR)xpr_swrite;
  182.     XprIO -> xpr_sflush    = (APTR)xpr_sflush;
  183.     XprIO -> xpr_update    = (APTR)xpr_update;
  184.     XprIO -> xpr_chkabort    = (APTR)xpr_chkabort;
  185.     XprIO -> xpr_gets    = (APTR)xpr_gets;
  186.     XprIO -> xpr_setserial    = (APTR)xpr_setserial;
  187.     XprIO -> xpr_ffirst    = (APTR)xpr_ffirst;
  188.     XprIO -> xpr_fnext    = (APTR)xpr_fnext;
  189.     XprIO -> xpr_finfo    = (APTR)xpr_finfo;
  190.     XprIO -> xpr_fseek    = (APTR)xpr_fseek;
  191.     XprIO -> xpr_extension    = 4;
  192.     XprIO -> xpr_options    = (APTR)xpr_options;
  193.     XprIO -> xpr_unlink    = (APTR)xpr_unlink;
  194.     XprIO -> xpr_squery    = (APTR)xpr_squery;
  195.     XprIO -> xpr_getptr    = (APTR)xpr_getptr;
  196.  
  197.         /* Try to open the library. */
  198.  
  199.     if(XProtocolBase = (struct Library *)OpenLibrary(LastXprLibrary,0))
  200.     {
  201.             /* Set up the library. */
  202.  
  203.         TransferBits = XProtocolSetup(XprIO);
  204.  
  205.             /* Successful initialization? */
  206.  
  207.     }
  208.  
  209.     return(TRUE);
  210. }
  211. BYTE
  212. SelectProtocol(void)
  213. {
  214.     strcpy(LastXprLibrary,"xprzmodem.library");
  215.     UseNewLibrary = TRUE;
  216.  
  217.     return(TRUE);
  218. }
  219. LONG __saveds __asm
  220. xpr_fopen(register __a0 UBYTE *FileName,register __a1 UBYTE *AccessMode)
  221. {
  222.     struct Buffer *File;
  223.  
  224.     Alerted = FALSE;
  225.  
  226.         /* Reset transfer counters. */
  227.  
  228.     ByteVal = ByteMax = TimeVal = TimeMax = 0;
  229.  
  230.         /* Remember file name. */
  231.  
  232.     strcpy(RealName,FileName);
  233.  
  234.         /* Build a new filename if neccessary. */
  235.  
  236.         if(!Uploading)
  237.         {
  238.             strcpy(RealName,"RAM:");
  239.             if(AddPart(RealName,FilePart(FileName),256))
  240.                 FileName = RealName;
  241.             else
  242.                 strcpy(RealName,FileName);
  243.         }
  244.  
  245.         /* Determine file transfer mode... */
  246.  
  247.     if(File = BufferOpen(FileName,AccessMode))
  248.     {
  249.         CurrentFile = File;
  250.     }
  251.  
  252.     DidTransfer = TRUE;
  253.  
  254.     return((LONG)File);
  255. }
  256. LONG __saveds __asm
  257. xpr_fclose(register __a0 struct Buffer *File)
  258. {
  259.     BYTE WriteAccess = File -> WriteAccess;
  260.  
  261.         /* Close the file and see what it brings... */
  262.  
  263.     if(BufferClose(File))
  264.     {
  265.             /* Did we receive or send a file? */
  266.  
  267.         if(WriteAccess)
  268.         {
  269.             LONG Size;
  270.  
  271.                 /* Did the file remain empty? */
  272.  
  273.             if(!(Size = GetFileSize(RealName)))
  274.             {
  275.                     /* Delete empty file. */
  276.  
  277.                 DeleteFile(RealName);
  278.             }
  279.             else
  280.             {
  281.                     /* Try to identify the file type. */
  282.  
  283.                 Identify(RealName);
  284.  
  285.             }
  286.         }
  287.         else
  288.         {
  289.                 /* Set the archived bit on files we uploaded? */
  290.  
  291.                 BPTR FileLock;
  292.  
  293.                     /* Get a lock on it. */
  294.  
  295.                 if(FileLock = Lock(RealName,ACCESS_READ))
  296.                 {
  297.                     struct FileInfoBlock __aligned FileInfo;
  298.  
  299.                         /* Examine the file. */
  300.  
  301.                     if(Examine(FileLock,&FileInfo))
  302.                     {
  303.                             /* Remove the lock. */
  304.  
  305.                         UnLock(FileLock);
  306.  
  307.                             /* Set the `archived' bit. */
  308.  
  309.                         SetProtection(RealName,FileInfo . fib_Protection | FIBF_ARCHIVE);
  310.                     }
  311.                     else
  312.                         UnLock(FileLock);
  313.                 }
  314.  
  315.         }
  316.     }
  317.  
  318.     RealName[0] = 0;
  319.  
  320.     CurrentFile = NULL;
  321.  
  322.     return(1);
  323. }
  324. LONG __saveds __asm
  325. xpr_fread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File)
  326. {
  327.     return(BufferRead(File,Buffer,Size * Count) / Size);
  328. }
  329.  
  330.     /* xpr_fwrite(APTR Buffer,LONG Size,LONG Count,struct Buffer *File):
  331.      *
  332.      *    Write a few bytes to a file.
  333.      */
  334.  
  335. LONG __saveds __asm
  336. xpr_fwrite(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File)
  337. {
  338.     return(BufferWrite(File,Buffer,Size * Count) / Size);
  339. }
  340.  
  341.     /* xpr_fseek(struct Buffer *File,LONG Offset,LONG Origin):
  342.      *
  343.      *    Move the read/write pointer in a file.
  344.      */
  345.  
  346. LONG __saveds __asm
  347. xpr_fseek(register __a0 struct Buffer *File,register __d0 LONG Offset,register __d1 LONG Origin)
  348. {
  349.     return(BufferSeek(File,Offset,Origin) ? 0 : -1);
  350. }
  351.  
  352.     /* xpr_sread(UBYTE *Buffer,LONG Size,LONG Timeout):
  353.      *
  354.      *    Read a few bytes from the serial port (including
  355.      *    timeouts).
  356.      */
  357.  
  358. ULONG __saveds __asm
  359. xpr_sread(register __a0 UBYTE *Buffer,register __d0 ULONG Size,register __d1 LONG Timeout)
  360. {
  361.         /* Valid parameters? */
  362.  
  363.     if(WriteSerReq && Size)
  364.     {
  365.             /* How many bytes are still in the serial buffer? */
  366.  
  367.         WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;
  368.  
  369.         DoIO(WriteSerReq);
  370.  
  371.             /* No timeout specified? Return as many bytes
  372.              * as are currently within the buffer.
  373.              */
  374.  
  375.         if(Timeout < 1)
  376.         {
  377.             register ULONG Length;
  378.  
  379.                 /* Are there any bytes in the buffer? */
  380.  
  381.             if(Length = WriteSerReq -> IOSer . io_Actual)
  382.             {
  383.                     /* More bytes available than we
  384.                      * were requested?
  385.                      */
  386.  
  387.                 if(Length > Size)
  388.                     Length = Size;
  389.  
  390.                     /* Fill the buffer. */
  391.  
  392.                 ReadSerReq -> IOSer . io_Command    = CMD_READ;
  393.                 ReadSerReq -> IOSer . io_Data        = Buffer;
  394.                 ReadSerReq -> IOSer . io_Length    = Length;
  395.                 ReadSerReq -> IOSer . io_Flags        = IOF_QUICK;
  396.  
  397.                 if(DoIO(ReadSerReq))
  398.                     Length = 0;
  399.             }
  400.  
  401.                 /* Return number of bytes read. */
  402.  
  403.             return(Length);
  404.         }
  405.         else
  406.         {
  407.                 /* Small enhancement: if the serial buffer holds as many
  408.                  * bytes as we may need, read the data immediately and
  409.                  * return as quickly as possible.
  410.                  */
  411.  
  412.             if(WriteSerReq -> IOSer . io_Actual >= Size)
  413.             {
  414.                 ReadSerReq -> IOSer . io_Command    = CMD_READ;
  415.                 ReadSerReq -> IOSer . io_Data        = Buffer;
  416.                 ReadSerReq -> IOSer . io_Length    = Size;
  417.                 ReadSerReq -> IOSer . io_Flags        = IOF_QUICK;
  418.  
  419.                 if(!DoIO(ReadSerReq))
  420.                     return(Size);
  421.                 else
  422.                     return(0);
  423.             }
  424.             else
  425.             {
  426.                 register ULONG SignalSet;
  427.  
  428.                     /* Set up the timer. */
  429.  
  430.                 TimerMsg -> tr_node . io_Command    = TR_ADDREQUEST;
  431.                 TimerMsg -> tr_time . tv_secs    = Timeout >= MILLION ? Timeout / MILLION : 0;
  432.                 TimerMsg -> tr_time . tv_micro    = Timeout % MILLION;
  433.  
  434.                     /* Set up the read request. */
  435.  
  436.                 ReadSerReq -> IOSer . io_Command    = CMD_READ;
  437.                 ReadSerReq -> IOSer . io_Data        = Buffer;
  438.                 ReadSerReq -> IOSer . io_Length    = Size;
  439.  
  440.                     /* Prevent early termination. */
  441.  
  442.                 SetSignal(0,(1 << ReadSerPort -> mp_SigBit) | (1 << TimerMsg -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit));
  443.  
  444.                     /* Start IO... */
  445.  
  446.                 SendIO(ReadSerReq);
  447.                 SendIO(TimerMsg);
  448.  
  449.                 FOREVER
  450.                 {
  451.                         /* Build signal mask. */
  452.  
  453.                     if(ZmodemStats)
  454.                         SignalSet = (1 << ReadSerPort -> mp_SigBit) | (1 << TimerMsg -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit) | (1 << ZmodemStats -> UserPort -> mp_SigBit);
  455.                     else
  456.                         SignalSet = (1 << ReadSerPort -> mp_SigBit) | (1 << TimerMsg -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit);
  457.  
  458.                         /* Wait for either of both IORequests to return. */
  459.  
  460.                     SignalSet = Wait(SignalSet);
  461.  
  462.                         /* Hit by timeout? */
  463.  
  464.                     if(SignalSet & (1 << TimerMsg -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit))
  465.                     {
  466.                             /* Abort the read request. */
  467.  
  468.                         AbortIO(ReadSerReq);
  469.                         WaitIO(ReadSerReq);
  470.  
  471.                             /* Remove the timer request. */
  472.  
  473.                         WaitIO(TimerMsg);
  474.  
  475.                             /* Did the driver receive any
  476.                              * data?
  477.                              */
  478.  
  479.                         if(!ReadSerReq -> IOSer . io_Actual)
  480.                         {
  481.                                 /* Take a second look and query the number of
  482.                                  * bytes ready to be received, there may
  483.                                  * still be some bytes in the buffer.
  484.                                  * Note: this depends on the way the
  485.                                  * driver handles read abort.
  486.                                  */
  487.  
  488.                             WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;
  489.  
  490.                             DoIO(WriteSerReq);
  491.  
  492.                                 /* Are there any bytes to transfer? */
  493.  
  494.                             if(WriteSerReq -> IOSer . io_Actual)
  495.                             {
  496.                                     /* Don't read more than actually wanted. */
  497.  
  498.                                 if(WriteSerReq -> IOSer . io_Actual < Size)
  499.                                     Size = WriteSerReq -> IOSer . io_Actual;
  500.  
  501.                                 ReadSerReq -> IOSer . io_Command    = CMD_READ;
  502.                                 ReadSerReq -> IOSer . io_Data        = Buffer;
  503.                                 ReadSerReq -> IOSer . io_Length    = Size;
  504.                                 ReadSerReq -> IOSer . io_Flags        = IOF_QUICK;
  505.  
  506.                                     /* Read the data. */
  507.  
  508.                                 if(!DoIO(ReadSerReq))
  509.                                     return(Size);
  510.                                 else
  511.                                     return(0);
  512.                             }
  513.                             else
  514.                                 return(0);
  515.                         }
  516.                         else
  517.                             return(ReadSerReq -> IOSer . io_Actual);
  518.                     }
  519.  
  520.                         /* Receive buffer filled? */
  521.  
  522.                     if(SignalSet & (1 << ReadSerPort -> mp_SigBit))
  523.                     {
  524.                         AbortIO(TimerMsg);
  525.                         WaitIO(TimerMsg);
  526.  
  527.                         WaitIO(ReadSerReq);
  528.  
  529.                         return(ReadSerReq -> IOSer . io_Actual);
  530.                     }
  531.  
  532.                         /* Check the transfer window for
  533.                          * possible user abort.
  534.                          */
  535.  
  536.                     if(ZmodemStats)
  537.                     {
  538.                         if(SignalSet & (1 << ZmodemStats -> UserPort -> mp_SigBit))
  539.                         {
  540.                             if(xpr_chkabort() == -1)
  541.                             {
  542.                                 AbortIO(ReadSerReq);
  543.                                 AbortIO(TimerMsg);
  544.  
  545.                                 WaitIO(ReadSerReq);
  546.                                 WaitIO(TimerMsg);
  547.  
  548.                                 SendAbort = TRUE;
  549.  
  550.                                 return(-1);
  551.                             }
  552.                         }
  553.                     }
  554.                 }
  555.             }
  556.         }
  557.     }
  558.  
  559.     return(0);
  560. }
  561. LONG __saveds __asm
  562. xpr_swrite(register __a0 UBYTE *Buffer,register __d0 LONG Size)
  563. {
  564.     if(WriteSerReq)
  565.     {
  566.         WriteSerReq -> IOSer . io_Command    = CMD_WRITE;
  567.         WriteSerReq -> IOSer . io_Data        = Buffer;
  568.         WriteSerReq -> IOSer . io_Length    = Size;
  569.         WriteSerReq -> IOSer . io_Flags    = IOF_QUICK;
  570.  
  571.         return((LONG)DoIO(WriteSerReq));
  572.     }
  573.     else
  574.         return(-1);
  575. }
  576.  
  577.     /* xpr_sflush():
  578.      *
  579.      *    Release the contents of all serial buffers.
  580.      */
  581.  
  582. LONG __saveds
  583. xpr_sflush()
  584. {
  585.     if(WriteSerReq)
  586.     {
  587.         WriteSerReq -> IOSer . io_Command = CMD_CLEAR;
  588.  
  589.         return((LONG)DoIO(WriteSerReq));
  590.     }
  591.     else
  592.         return(-1);
  593. }
  594.  
  595.     /* GetSeconds(UBYTE *String):
  596.      *
  597.      *    Tries to turn a string of the format hh:mm:ss into
  598.      *    an integer number.
  599.      */
  600.  
  601. STATIC LONG __regargs
  602. GetSeconds(UBYTE *String)
  603. {
  604.     UBYTE    Buffer[20];
  605.     LONG    Seconds = 0;
  606.  
  607.     memset(Buffer,0,20);
  608.  
  609.     strcpy(Buffer,String);
  610.  
  611.     Seconds += atol(&Buffer[6]);
  612.  
  613.     Buffer[5] = 0;
  614.  
  615.     Seconds += atol(&Buffer[3]) * 60;
  616.  
  617.     Buffer[2] = 0;
  618.  
  619.     Seconds += atol(&Buffer[0]) * 3600;
  620.  
  621.     return(Seconds);
  622. }
  623.  
  624.     /* TruncateName(UBYTE *FileName):
  625.      *
  626.      *    Truncates a file name to a maximum of 48 characters.
  627.      */
  628.  
  629. STATIC UBYTE * __regargs
  630. TruncateName(UBYTE *FileName)
  631. {
  632.     WORD Len = strlen(FileName);
  633.  
  634.     if(Len > 48)
  635.     {
  636.         WORD i;
  637.  
  638.         for(i = Len - 48 ; i < Len ; i++)
  639.         {
  640.             if(i >= Len - 44 && FileName[i] == '/')
  641.             {
  642.                 STATIC UBYTE NameBuffer[256];
  643.  
  644.                 strcpy(NameBuffer,".../");
  645.  
  646.                 strcat(NameBuffer,&FileName[i + 1]);
  647.  
  648.                 return(NameBuffer);
  649.             }
  650.         }
  651.     }
  652.  
  653.     return(FileName);
  654. }
  655.  
  656.     /* CalculateBlocks(LONG Size,LONG BlockSize):
  657.      *
  658.      *    Calculate the number of blocks a file will
  659.      *    occupy if saved to a disk.
  660.      */
  661.  
  662. STATIC LONG __regargs
  663. CalculateBlocks(LONG Size,LONG BlockSize)
  664. {
  665.     LONG    Blocks        = 1;        /* One for the file header. */
  666.     BYTE    HasExtension    = FALSE,    /* No extension block yet. */
  667.         Extension    = 0;        /* How many block pointers yet. */
  668.  
  669.         /* Round to next block. */
  670.  
  671.     Size = ((Size + BlockSize - 1) / BlockSize) * BlockSize;
  672.  
  673.     while(Size)
  674.     {
  675.             /* Add another block. */
  676.  
  677.         Blocks++;
  678.  
  679.             /* Subtract another block. */
  680.  
  681.         Size -= BlockSize;
  682.  
  683.             /* Add another block pointer, if 72 have been
  684.              * added, add another extension block.
  685.              */
  686.  
  687.         if((Extension++) == 72)
  688.         {
  689.                 /* If no extension block has been generated
  690.                  * yet, we were running on the block pointers
  691.                  * of the file header itself.
  692.                  */
  693.  
  694.             if(!HasExtension)
  695.                 HasExtension = TRUE;
  696.             else
  697.                 Blocks++;
  698.  
  699.                 /* Reset extension block counter. */
  700.  
  701.             Extension = 0;
  702.         }
  703.     }
  704.  
  705.     return(Blocks);
  706. }
  707. LONG __saveds __asm
  708. xpr_update(register __a0 struct XPR_UPDATE *UpdateInfo)
  709. {
  710.     if(UpdateInfo)
  711.     {
  712.         BYTE    NewByte = FALSE,
  713.             NewTime = FALSE;
  714.  
  715.  
  716.         if((UpdateInfo -> xpru_updatemask & XPRU_ERRORMSG) && UpdateInfo -> xpru_errormsg)
  717.             AddErrorMessage(UpdateInfo -> xpru_errormsg);
  718.  
  719.         if((UpdateInfo -> xpru_updatemask & XPRU_FILENAME) && UpdateInfo -> xpru_filename)
  720.         {
  721.             if(!Uploading)
  722.             {
  723.                 if(!RealName[0])
  724.                 {
  725.                         strcpy(RealName,"RAM:");
  726.  
  727.                     if(!AddPart(RealName,FilePart(UpdateInfo -> xpru_filename),256))
  728.                         strcpy(RealName,UpdateInfo -> xpru_filename);
  729.                 }
  730.  
  731.                 TransferInfo(19, 0,"%-48.48s",TruncateName(RealName));
  732.             }
  733.             else
  734.                 TransferInfo(19, 0,"%-48.48s",TruncateName(UpdateInfo -> xpru_filename));
  735.         }
  736.  
  737.         if((UpdateInfo -> xpru_updatemask & XPRU_FILESIZE) && UpdateInfo -> xpru_filesize != -1)
  738.         {
  739.             if(CurrentFile -> DirLock && CurrentFile -> InfoData . id_NumBlocks && CurrentFile -> InfoData . id_BytesPerBlock)
  740.                 TransferInfo(19, 1,ConvNumber10,UpdateInfo -> xpru_filesize);
  741.             else
  742.                 TransferInfo(19, 1,ConvNumber,UpdateInfo -> xpru_filesize);
  743.  
  744.             if(ByteMax = UpdateInfo -> xpru_filesize)
  745.                 NewByte = TRUE;
  746.         }
  747.  
  748.         if((UpdateInfo -> xpru_updatemask & XPRU_BYTES) && UpdateInfo -> xpru_bytes != -1)
  749.         {
  750.             TransferInfo(19, 4,ConvNumber,ByteVal = UpdateInfo -> xpru_bytes);
  751.  
  752.             if(ByteMax)
  753.                 NewByte = TRUE;
  754.  
  755.             if(CurrentFile -> DirLock && !Uploading)
  756.             {
  757.                 if(CurrentFile -> InfoData . id_NumBlocks && CurrentFile -> InfoData . id_BytesPerBlock)
  758.                 {
  759.                     if(ByteMax)
  760.                     {
  761.                         register LONG Blocks = CalculateBlocks(ByteMax,CurrentFile -> InfoData . id_BytesPerBlock),Space = CurrentFile -> InfoData . id_NumBlocks - CurrentFile -> InfoData . id_NumBlocksUsed;
  762.  
  763.                         TransferInfo(19, 2,LocaleString(BYTES_FULL_TXT),Space * CurrentFile -> InfoData . id_BytesPerBlock,100 * (CurrentFile -> InfoData . id_NumBlocksUsed) / CurrentFile -> InfoData . id_NumBlocks,(Space < Blocks) ? LocaleString(FILE_MAY_NOT_FIT_TXT) : "");
  764.  
  765.                             /* Tell the user that he might be
  766.                              * running into trouble.
  767.                              */
  768.  
  769.                         if(!Alerted && (Space < Blocks))
  770.                         {
  771.                             Alerted = TRUE;
  772.  
  773.                             BumpWindow(ZmodemStats);
  774.  
  775.                         }
  776.                     }
  777.                     else
  778.                         TransferInfo(19, 2,LocaleString(BYTES_FULL_TXT),(CurrentFile -> InfoData . id_NumBlocks - CurrentFile -> InfoData . id_NumBlocksUsed) * CurrentFile -> InfoData . id_BytesPerBlock,100 * (CurrentFile -> InfoData . id_NumBlocksUsed) / CurrentFile -> InfoData . id_NumBlocks);
  779.                 }
  780.             }
  781.         }
  782.  
  783.         if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKS) && UpdateInfo -> xpru_blocks != -1)
  784.             TransferInfo(19, 5,ConvNumber,UpdateInfo -> xpru_blocks);
  785.  
  786.         if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKSIZE) && UpdateInfo -> xpru_blocksize != -1)
  787.             TransferInfo(19, 6,ConvNumber,UpdateInfo -> xpru_blocksize);
  788.  
  789.         if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKCHECK) && UpdateInfo -> xpru_blockcheck)
  790.             TransferInfo(19, 7,"%-12.12s",UpdateInfo -> xpru_blockcheck);
  791.  
  792.         if((UpdateInfo -> xpru_updatemask & XPRU_EXPECTTIME) && UpdateInfo -> xpru_expecttime)
  793.         {
  794.             TransferInfo(19, 9,"%-12.12s",UpdateInfo -> xpru_expecttime);
  795.  
  796.             if(TimeMax = GetSeconds((UBYTE *)UpdateInfo -> xpru_expecttime))
  797.                 NewTime = TRUE;
  798.         }
  799.  
  800.         if((UpdateInfo -> xpru_updatemask & XPRU_ELAPSEDTIME) && UpdateInfo -> xpru_elapsedtime)
  801.         {
  802.             TransferInfo(19,10,"%-12.12s",UpdateInfo -> xpru_elapsedtime);
  803.  
  804.             TimeVal = GetSeconds((UBYTE *)UpdateInfo -> xpru_elapsedtime);
  805.  
  806.             if(TimeMax)
  807.                 NewTime = TRUE;
  808.         }
  809.  
  810.         if((UpdateInfo -> xpru_updatemask & XPRU_DATARATE) && UpdateInfo -> xpru_datarate != -1)
  811.             TransferInfo(54, 4,ConvNumber,UpdateInfo -> xpru_datarate);
  812.  
  813.         if((UpdateInfo -> xpru_updatemask & XPRU_CHARDELAY) && UpdateInfo -> xpru_chardelay != -1)
  814.             TransferInfo(54, 5,ConvNumber,UpdateInfo -> xpru_chardelay);
  815.  
  816.         if((UpdateInfo -> xpru_updatemask & XPRU_PACKETDELAY) && UpdateInfo -> xpru_packetdelay != -1)
  817.             TransferInfo(54, 6,ConvNumber,UpdateInfo -> xpru_packetdelay);
  818.  
  819.         if((UpdateInfo -> xpru_updatemask & XPRU_PACKETTYPE) && UpdateInfo -> xpru_packettype != -1)
  820.         {
  821.             if(UpdateInfo -> xpru_packettype > 31 && UpdateInfo -> xpru_packettype < 256)
  822.             {
  823.                 if(ValidTab[UpdateInfo -> xpru_packettype])
  824.                     TransferInfo(54, 7,"`%lc'",UpdateInfo -> xpru_packettype);
  825.                 else
  826.                     TransferInfo(54, 7,ConvNumber,UpdateInfo -> xpru_packettype);
  827.             }
  828.             else
  829.                 TransferInfo(54, 7,ConvNumber,UpdateInfo -> xpru_packettype);
  830.         }
  831.  
  832.         if((UpdateInfo -> xpru_updatemask & XPRU_ERRORS) && UpdateInfo -> xpru_errors != -1)
  833.             TransferInfo(54, 9,ConvNumber,UpdateInfo -> xpru_errors);
  834.  
  835.         if((UpdateInfo -> xpru_updatemask & XPRU_TIMEOUTS) && UpdateInfo -> xpru_timeouts != -1)
  836.             TransferInfo(54,10,ConvNumber,UpdateInfo -> xpru_timeouts);
  837.  
  838.         if(ZmodemStats)
  839.         {
  840.             if(NewByte)
  841.             {
  842.                 ShowStats(TransferGadgetArray[2],ByteVal,ByteMax);
  843.  
  844.                 if(ByteMax)
  845.                     ShowString(TransferGadgetArray[2],"%3ld%%",(100 * ByteVal) / ByteMax);
  846.                 else
  847.                     ShowString(TransferGadgetArray[2],"%3ld%%",0);
  848.             }
  849.  
  850.             if(NewTime)
  851.             {
  852.                 LONG TimeDif = (TimeMax - TimeVal) < 0 ? 0 : TimeMax - TimeVal;
  853.  
  854.                 ShowStats(TransferGadgetArray[3],TimeDif,TimeMax);
  855.  
  856.                 ShowString(TransferGadgetArray[3],"%2ld:%02ld:%02ld",TimeDif / 3600,(TimeDif / 60) % 60,TimeDif % 60);
  857.             }
  858.         }
  859.     }
  860.  
  861.     return(1);
  862. }
  863. LONG __saveds
  864. xpr_chkabort()
  865. {
  866.     LONG Result = 0;
  867.  
  868.     if(ZmodemStats)
  869.     {
  870.         struct IntuiMessage    *Massage;
  871.         ULONG             Class,Code;
  872.         struct Gadget        *Gadget;
  873.  
  874.         while(Massage = (struct IntuiMessage *)GT_GetIMsg(ZmodemStats -> UserPort))
  875.         {
  876.             Class    = Massage -> Class;
  877.             Code    = Massage -> Code;
  878.             Gadget    = (struct Gadget *)Massage -> IAddress;
  879.  
  880.             GT_ReplyIMsg(Massage);
  881.  
  882.             if(!Result)
  883.             {
  884.                     if(Class == IDCMP_CLOSEWINDOW)
  885.                     {
  886.  
  887.                         TransferAborted = TRUE;
  888.  
  889.                         Result = -1;
  890.                     }
  891.                 
  892.             }
  893.         }
  894.     }
  895.  
  896.     return(Result);
  897. }
  898. LONG __saveds __asm
  899. xpr_setserial(register __d0 LONG Status)
  900. {
  901.     if(WriteSerReq)
  902.     {
  903.         STATIC LONG XprBauds[12] =
  904.         {
  905.                110,
  906.                300,
  907.               1200,
  908.               2400,
  909.               4800,
  910.               9600,
  911.              19200,
  912.              31250,
  913.              38400,
  914.              57600,
  915.              76800,
  916.             115200
  917.         };
  918.  
  919.         LONG Return,i;
  920.  
  921.         WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;
  922.  
  923.         DoIO(WriteSerReq);
  924.  
  925.         Return = WriteSerReq -> io_SerFlags & 0xFF;
  926.  
  927.         if(WriteSerReq -> io_ExtFlags & SEXTF_MSPON)
  928.             Return |= ST_PARTYMARKON;
  929.  
  930.         if(WriteSerReq -> io_ExtFlags & SEXTF_MARK)
  931.             Return |= ST_PARTYMARK;
  932.  
  933.         if(WriteSerReq -> io_StopBits == 2)
  934.             Return |= ST_2BITS;
  935.  
  936.         if(WriteSerReq -> io_ReadLen == 7)
  937.             Return |= ST_READ7;
  938.  
  939.         if(WriteSerReq -> io_WriteLen == 7)
  940.             Return |= ST_WRITE7;
  941.  
  942.         for(i = 0 ; i < 12 ; i++)
  943.         {
  944.             if(WriteSerReq -> io_Baud <= XprBauds[i])
  945.             {
  946.                 Return |= (i << 16);
  947.  
  948.                 break;
  949.             }
  950.         }
  951.  
  952.         if(Status != -1)
  953.         {
  954.             WriteSerReq -> IOSer . io_Command    = SDCMD_SETPARAMS;
  955.  
  956.             WriteSerReq -> io_SerFlags        = Status & 0xFF;
  957.             WriteSerReq -> io_ExtFlags        = 0;
  958.  
  959.             if(Status & ST_PARTYMARKON)
  960.                 WriteSerReq -> io_ExtFlags |= SEXTF_MSPON;
  961.  
  962.             if(Status & ST_PARTYMARK)
  963.                 WriteSerReq -> io_ExtFlags |= SEXTF_MARK;
  964.  
  965.             if(Status & ST_2BITS)
  966.                 WriteSerReq -> io_StopBits = 2;
  967.             else
  968.                 WriteSerReq -> io_StopBits = 1;
  969.  
  970.             if(Status & ST_READ7)
  971.                 WriteSerReq -> io_ReadLen = 7;
  972.             else
  973.                 WriteSerReq -> io_ReadLen = 8;
  974.  
  975.             if(Status & ST_WRITE7)
  976.                 WriteSerReq -> io_WriteLen = 7;
  977.             else
  978.                 WriteSerReq -> io_WriteLen = 8;
  979.  
  980.             DoIO(WriteSerReq);
  981.  
  982.             ReadSerReq -> io_SerFlags    = WriteSerReq -> io_SerFlags;
  983.             ReadSerReq -> io_ExtFlags    = WriteSerReq -> io_ExtFlags;
  984.  
  985.             ReadSerReq -> io_StopBits    = WriteSerReq -> io_StopBits;
  986.             ReadSerReq -> io_ReadLen    = WriteSerReq -> io_ReadLen;
  987.             ReadSerReq -> io_WriteLen    = WriteSerReq -> io_WriteLen;
  988.         }
  989.  
  990.         return(Return);
  991.     }
  992.     else
  993.         return(-1);
  994. }
  995.  
  996.     /* xpr_ffirst(UBYTE *Buffer,UBYTE *Pattern):
  997.      *
  998.      *    Batch file upload: find the first matching file and return
  999.      *    its name.
  1000.      */
  1001.  
  1002. LONG __saveds __asm
  1003. xpr_ffirst(register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern)
  1004. {
  1005.     if(MultipleFiles)
  1006.     {
  1007.         FileCount = 0;
  1008.  
  1009.         strcpy(Buffer,FileArg[FileCount++]);
  1010.  
  1011.         return(1);
  1012.     }
  1013.     else
  1014.     {
  1015.         FileMatch = TRUE;
  1016.  
  1017.         if(!MatchFirst(Pattern,FileAnchor))
  1018.         {
  1019.             if(FileAnchor -> ap_Info . fib_DirEntryType < 0)
  1020.             {
  1021.                 strcpy(Buffer,FileAnchor -> ap_Info . fib_FileName);
  1022.  
  1023.                 return(1);
  1024.             }
  1025.             else
  1026.             {
  1027.                 while(!MatchNext(FileAnchor))
  1028.                 {
  1029.                     if(FileAnchor -> ap_Info . fib_DirEntryType < 0)
  1030.                     {
  1031.                         strcpy(Buffer,FileAnchor -> ap_Info . fib_FileName);
  1032.  
  1033.                         return(1);
  1034.                     }
  1035.                 }
  1036.             }
  1037.         }
  1038.     }
  1039.  
  1040.     return(0);
  1041. }
  1042.  
  1043.     /* xpr_fnext(LONG OldState,UBYTE *Buffer,UBYTE *Pattern):
  1044.      *
  1045.      *    Batch file upload: find the next matching file
  1046.      *    - if any - and return its name.
  1047.      */
  1048.  
  1049. LONG __saveds __asm
  1050. xpr_fnext(register __d0 LONG OldState,register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern)
  1051. {
  1052.     if(MultipleFiles)
  1053.     {
  1054.         if(FileCount < FileCountMax)
  1055.         {
  1056.             strcpy(Buffer,FileArg[FileCount++]);
  1057.  
  1058.             return(1);
  1059.         }
  1060.     }
  1061.     else
  1062.     {
  1063.         FileMatch = TRUE;
  1064.  
  1065.         while(!MatchNext(FileAnchor))
  1066.         {
  1067.             if(FileAnchor -> ap_Info . fib_DirEntryType < 0)
  1068.             {
  1069.                 strcpy(Buffer,FileAnchor -> ap_Info . fib_FileName);
  1070.  
  1071.                 return(1);
  1072.             }
  1073.         }
  1074.     }
  1075.  
  1076.     return(0);
  1077. }
  1078. LONG __saveds __asm
  1079. xpr_finfo(register __a0 UBYTE *FileName,register __d0 LONG InfoType)
  1080. {
  1081.     BPTR FileLock;
  1082.  
  1083.     switch(InfoType)
  1084.     {
  1085.         case 1:    if(!Uploading)
  1086.             {
  1087.                     strcpy(RealName,"RAM:");
  1088.  
  1089.                 if(AddPart(RealName,FilePart(FileName),256))
  1090.                     FileName = RealName;
  1091.             }
  1092.  
  1093.             if(FileLock = Lock(FileName,ACCESS_READ))
  1094.             {
  1095.                 struct FileInfoBlock __aligned    FileInfo;
  1096.                 register LONG            Size;
  1097.  
  1098.                 if(Examine(FileLock,&FileInfo))
  1099.                     Size = FileInfo . fib_Size;
  1100.                 else
  1101.                     Size = 0;
  1102.  
  1103.                 UnLock(FileLock);
  1104.  
  1105.                 return(Size);
  1106.             }
  1107.  
  1108.             break;
  1109.  
  1110.         case 2:    return(BinaryTransfer ? 1 : 2);
  1111.     }
  1112.  
  1113.     return(0);
  1114. }
  1115. STATIC BYTE __regargs
  1116. GetOptionMode(struct xpr_option *Option)
  1117. {
  1118.     if(Option)
  1119.     {
  1120.         if(!Stricmp(Option -> xpro_value,"OFF"))
  1121.             return(FALSE);
  1122.  
  1123.         if(!Stricmp(Option -> xpro_value,"FALSE"))
  1124.             return(FALSE);
  1125.  
  1126.         if(!Stricmp(Option -> xpro_value,"F"))
  1127.             return(FALSE);
  1128.  
  1129.         if(!Stricmp(Option -> xpro_value,"NO"))
  1130.             return(FALSE);
  1131.  
  1132.         if(!Stricmp(Option -> xpro_value,"N"))
  1133.             return(FALSE);
  1134.  
  1135.  
  1136.         if(!Stricmp(Option -> xpro_value,"ON"))
  1137.             return(TRUE);
  1138.  
  1139.         if(!Stricmp(Option -> xpro_value,"TRUE"))
  1140.             return(TRUE);
  1141.  
  1142.         if(!Stricmp(Option -> xpro_value,"T"))
  1143.             return(TRUE);
  1144.  
  1145.         if(!Stricmp(Option -> xpro_value,"YES"))
  1146.             return(TRUE);
  1147.  
  1148.         if(!Stricmp(Option -> xpro_value,"Y"))
  1149.             return(TRUE);
  1150.     }
  1151.  
  1152.     return(FALSE);
  1153. }
  1154. VOID __stdargs
  1155. TransferInfo(WORD X,WORD Y,BYTE *String,...)
  1156. {
  1157.     if(String && ZmodemStats)
  1158.     {
  1159.         va_list    VarArgs;
  1160.  
  1161.         va_start(VarArgs,String);
  1162.         VSPrintf(SharedBuffer,String,VarArgs);
  1163.         va_end(VarArgs);
  1164.  
  1165.         if(!strncmp(String,"%10ld ",6))
  1166.         {
  1167.             strcat(SharedBuffer,"                                   ");
  1168.  
  1169.             SharedBuffer[47] = 0;
  1170.         }
  1171.         else
  1172.         {
  1173.             if(!Stricmp(String,"%ld") || !Stricmp(String,"%10ld") || !Stricmp(String,"`%lc'"))
  1174.             {
  1175.                 strcat(SharedBuffer,"              ");
  1176.  
  1177.                 SharedBuffer[11] = 0;
  1178.             }
  1179.         }
  1180.  
  1181.         Move(ZmodemStats -> RPort,ORIGIN_X + 4 + X * 8,ORIGIN_Y + 6 + LineOffsets[Y]);
  1182.         Text(ZmodemStats -> RPort,SharedBuffer,strlen(SharedBuffer));
  1183.     }
  1184. }
  1185.  
  1186. STATIC VOID __stdargs
  1187. TransferText(WORD Y,UBYTE *String,...)
  1188. {
  1189.     va_list    VarArgs;
  1190.  
  1191.     va_start(VarArgs,String);
  1192.     VSPrintf(SharedBuffer,String,VarArgs);
  1193.     va_end(VarArgs);
  1194.  
  1195.     Move(ZmodemStats -> RPort,ORIGIN_X,ORIGIN_Y + 6 + LineOffsets[Y]);
  1196.     Text(ZmodemStats -> RPort,SharedBuffer,strlen(SharedBuffer));
  1197. }
  1198. VOID
  1199. ShowStats(struct Gadget *Gadget,LONG Value,LONG Max)
  1200. {
  1201.     if(ZmodemStats)
  1202.     {
  1203.         struct RastPort    *RPort = ZmodemStats -> RPort;
  1204.         LONG         MaxWidth = Gadget -> Width - 4,Width;
  1205.  
  1206.         if(Max < 1)
  1207.             Max = 0;
  1208.  
  1209.         if(Value > Max)
  1210.             Value = Max;
  1211.  
  1212.         if((Width = (MaxWidth * Value) / Max) > 0)
  1213.         {
  1214.             BYTE FgPen = RPort -> FgPen;
  1215.  
  1216.             if(Width != MaxWidth)
  1217.             {
  1218.                 SetAPen(RPort,0);
  1219.  
  1220.                 RectFill(RPort,Gadget -> LeftEdge + 2 + Width - 1,Gadget -> TopEdge + 1,Gadget -> LeftEdge + Gadget -> Width - 3,Gadget -> TopEdge + Gadget -> Height - 2);
  1221.             }
  1222.  
  1223.                SetAPen(RPort,7);
  1224.  
  1225.             RectFill(RPort,Gadget -> LeftEdge + 2,Gadget -> TopEdge + 1,Gadget -> LeftEdge + Width + 1,Gadget -> TopEdge + Gadget -> Height - 2);
  1226.  
  1227.             SetAPen(RPort,FgPen);
  1228.         }
  1229.         else
  1230.         {
  1231.             BYTE FgPen = RPort -> FgPen;
  1232.  
  1233.             SetAPen(RPort,0);
  1234.  
  1235.             RectFill(RPort,Gadget -> LeftEdge + 2,Gadget -> TopEdge + 1,Gadget -> LeftEdge + Gadget -> Width - 3,Gadget -> TopEdge + Gadget -> Height - 2);
  1236.  
  1237.             SetAPen(RPort,FgPen);
  1238.         }
  1239.     }
  1240. }
  1241.  
  1242. VOID __stdargs
  1243. ShowString(struct Gadget *Gadget,UBYTE *String,...)
  1244. {
  1245.      BYTE FgPen,DrawMode;
  1246.     if(ZmodemStats)
  1247.     {
  1248.         va_list         VarArgs;
  1249.         WORD         Len;
  1250.         struct RastPort    *RPort = ZmodemStats -> RPort;
  1251.  
  1252.         va_start(VarArgs,String);
  1253.         VSPrintf(SharedBuffer,String,VarArgs);
  1254.         va_end(VarArgs);
  1255.  
  1256.         Len = strlen(SharedBuffer);
  1257.  
  1258.         Move(RPort,Gadget -> LeftEdge + 2 + (Gadget -> Width - 4 - 8 * Len) / 2,Gadget -> TopEdge + 1 + (Gadget -> Height - 2 - 8) / 2 + 6);
  1259.  
  1260.             FgPen = RPort -> FgPen;DrawMode = RPort -> DrawMode;
  1261.  
  1262.                SetAPen(RPort,2);
  1263.  
  1264.             SetDrMd(RPort,JAM1);
  1265.  
  1266.             Text(RPort,SharedBuffer,Len);
  1267.  
  1268.             SetAPen(RPort,FgPen);
  1269.             SetDrMd(RPort,DrawMode);
  1270.         
  1271.     }
  1272. }
  1273.